﻿/*******************************************************************************

    Units of Measure for C# applications

    Copyright (C) 2013 Marek Aniola

    This program is provided to you under the terms of the license
    as published at http://unitsofmeasure.codeplex.com/license


********************************************************************************/
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Man.UnitsOfMeasure;

namespace UnitsOfMeasure.Test
{
	[TestClass]
	public class MeasureTests
	{
		[TestClass]
		public class StaticConstructor
		{
			private struct StructWithNoTypeHost
			{
				// contents does not matter
			}

			[TestMethod]
			public void NotImplementedTypeHostThrowsException()
			{
				bool exceptionThrown = false;
				try
				{
					Measure<StructWithNoTypeHost> m = new Measure<StructWithNoTypeHost>(Magnitude.Mass);
				}
				catch (System.TypeInitializationException e)
				{
					exceptionThrown = e.InnerException is System.NotImplementedException;
				}
				Assert.IsTrue(exceptionThrown);
			}
		}

		[TestClass]
		public class Constructor
		{
			[TestMethod]
			public void ScaleZeroThrowsException()
			{
				Assert.IsTrue(
					ScaleZeroThrowsException<float>(0.0F) &&
					ScaleZeroThrowsException<double>(0.0D) &&
					ScaleZeroThrowsException<decimal>(0.0M)
				);
			}

			private bool ScaleZeroThrowsException<T>(T zero) where T : struct
			{
				bool exceptionThrown = false;
				try
				{
					Measure<T> m = new Measure<T>(zero, new Dimension(Magnitude.Temperature));
				}
				catch (System.ArgumentException)
				{
					exceptionThrown = true;
				}
				return exceptionThrown;
			}
		}

		[TestClass]
		public class Operators
		{
			[TestMethod]
			public void ScaleShiftAndEquality()
			{
				{
					Measure<double> kelvin = new Measure<double>(Magnitude.Temperature);
					Measure<double> celsius = kelvin - 273.15D;
					Measure<double> fahrenheit = 9.0D * celsius / 5.0D + 32.0D;
					Measure<double> fahrenheitTheOtherWay = new Measure<double>(9.0D / 5.0D, Magnitude.Temperature, -273.15D * (9.0D / 5.0D) + 32.0D);
					Assert.IsTrue(fahrenheit == fahrenheitTheOtherWay);
				}
				{
					Measure<float> kelvin = new Measure<float>(Magnitude.Temperature);
					Measure<float> celsius = kelvin - 273.15F;
					Measure<float> fahrenheit = 9.0F * celsius / 5.0F + 32.0F;
					Measure<float> fahrenheitTheOtherWay = new Measure<float>(9.0F / 5.0F, Magnitude.Temperature, -273.15F * (9.0F / 5.0F) + 32.0F);
					Assert.IsTrue(fahrenheit == fahrenheitTheOtherWay);
				}
				{
					Measure<decimal> kelvin = new Measure<decimal>(Magnitude.Temperature);
					Measure<decimal> celsius = kelvin - 273.15M;
					Measure<decimal> fahrenheit = 9.0M * celsius / 5.0M + 32.0M;
					Measure<decimal> fahrenheitTheOtherWay = new Measure<decimal>(9.0M / 5.0M, Magnitude.Temperature, -273.15M * (9.0M / 5.0M) + 32.0M);
					Assert.IsTrue(fahrenheit == fahrenheitTheOtherWay);
				}
			}
			[TestMethod]
			public void ProductQuotientAndEquality()
			{
				Dimension heatCapacityDimension = new Dimension(2/*length*/, -2/*time*/, 1/*mass*/, -1/*temperature*/, 0, 0, 0, 0);
				{
					Measure<double> heat = EnergyMeasure<double>();
					Measure<double> temperature = new Measure<double>(Magnitude.Temperature);
					Measure<double> heatCapacity = heat / temperature;
					Assert.IsTrue(heatCapacity == (new Measure<double>(1.0D, heatCapacityDimension, 0.0D)));
				}
				{
					Measure<float> heat = EnergyMeasure<float>();
					Measure<float> temperature = new Measure<float>(Magnitude.Temperature);
					Measure<float> heatCapacity = heat / temperature;
					Assert.IsTrue(heatCapacity == (new Measure<float>(1.0F, heatCapacityDimension, 0.0F)));
				}
				{
					Measure<decimal> heat = EnergyMeasure<decimal>();
					Measure<decimal> temperature = new Measure<decimal>(Magnitude.Temperature);
					Measure<decimal> heatCapacity = heat / temperature;
					Assert.IsTrue(heatCapacity == (new Measure<decimal>(1.0M, heatCapacityDimension, 0.0M)));
				}
			}

			private Measure<T> EnergyMeasure<T>() where T : struct
			{
				Measure<T> meter = new Measure<T>(Magnitude.Length);
				Measure<T> second = new Measure<T>(Magnitude.Time);
				Measure<T> kilogram = new Measure<T>(Magnitude.Mass);
				Measure<T> newton = kilogram * meter / second / second;
				Measure<T> joule = newton * meter;
				return joule;
			}
		}
	}
}
